package com.wangshuqi.spring;

import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

public class AnnotationApplicationContext {

    private Class configClass;

    private Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();
    private Map<String, Object> singletonObjects = new ConcurrentHashMap();
    private ArrayList<BeanPostProcessor> beanPostProcessorList = new ArrayList<>();

    public AnnotationApplicationContext(Class configClass) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
        this.configClass = configClass;
        // 判断有没有@ComponentScan注解
        if (configClass.isAnnotationPresent(ComponentScan.class)) {
            // 扫描@ComponentScan指定的包
            ComponentScan componentScanAnnotation = (ComponentScan) configClass.getAnnotation(ComponentScan.class);
            String path = componentScanAnnotation.value(); // 扫描到的目录：com.wangshuqi.app
            String replacePath = path.replace(".", "/"); // com/wangshuqi/app
            URL resource = configClass.getClassLoader().getResource(replacePath); // D:/Java-learn/spring/spring/out/production/my-spring/com/wangshuqi/app
            File file = new File(resource.getFile());
            if (file.isDirectory()) {
                File[] files = file.listFiles();
                for (File f : files) {
                    String fileName = f.getAbsolutePath();
                    if (fileName.endsWith(".class")) {
                        String className = fileName.substring(fileName.lastIndexOf(File.separator) + 1, fileName.indexOf(".class"));
                        Class<?> loadClass = configClass.getClassLoader().loadClass(path.concat("." + className)); // 加载类文件
                        if (loadClass.isAnnotationPresent(Component.class)) {
                            // 判断加载类是否实现了BeanPostProcessor接口
                            if (BeanPostProcessor.class.isAssignableFrom(loadClass)) {
                                beanPostProcessorList.add((BeanPostProcessor) loadClass.newInstance());
                            }
                            Component component = loadClass.getAnnotation(Component.class);
                            String beanName = component.value();
                            if (beanName == null || beanName.length() == 0) {
                                beanName = decapitalize(className);
                            }
                            BeanDefinition beanDefinition = new BeanDefinition();
                            beanDefinition.setBeanName(beanName);
                            beanDefinition.setType(loadClass);
                            if (loadClass.isAnnotationPresent(Scope.class)) {
                                Scope scope = loadClass.getAnnotation(Scope.class);
                                beanDefinition.setScope(scope.value());
                            } else {
                                beanDefinition.setScope("singleton");
                            }
                            beanDefinitionMap.put(beanName, beanDefinition);
                        }
                    }
                }
            }
        }
        // 创建bean对象并放入单例池
        for (String beanName : beanDefinitionMap.keySet()) {
            BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
            if (beanDefinition.getScope().equals("singleton")) {
                if (!singletonObjects.containsKey(beanName)) {
                    singletonObjects.put(beanName, createBean(beanName, beanDefinition));
                }
            }
        }
    }

    /*
     * 创建bean对象
     * */
    public Object createBean(String beanName, BeanDefinition beanDefinition) {
        Class type = beanDefinition.getType();
        try {
            Object bean = type.getConstructor().newInstance();
            // 依赖注入
            for (Field field : type.getDeclaredFields()) {
                if (field.isAnnotationPresent(Autowired.class)) {
                    field.setAccessible(true);
                    field.set(bean, getBean(field.getName()));
                }
            }
            // Aware回调机制
            if (bean instanceof BeanNameAware) {
                ((BeanNameAware) bean).setBeanName(beanName);
            }
            // 初始化前
            for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
                bean = beanPostProcessor.postProcessBeforeInitialization(bean, beanName);
            }
            // bean的初始化
            if (bean instanceof InitializingBean) {
                ((InitializingBean) bean).afterPropertiesSet();
            }
            // 初始化后
            for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
                bean = beanPostProcessor.postProcessAfterInitialization(bean, beanName);
            }

            return bean;
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /*
     * 根据beanName获取对应bean
     * */
    public Object getBean(String beanName) {
        BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
        if (beanDefinition == null) {
            throw new NullPointerException("object bean is null");
        }
        if (beanDefinition.getScope().equals("singleton")) {
            Object bean = singletonObjects.get(beanName);
            if (bean == null) {
                bean = createBean(beanName, beanDefinition);
                singletonObjects.put(beanName, bean);
            }
            return bean;
        } else {
            return createBean(beanName, beanDefinition);
        }
    }

    /*
     * 默认beanName生成规则
     * */
    public String decapitalize(String name) {
        if (name == null || name.length() == 0) {
            return name;
        }
        if (name.length() > 1 && Character.isUpperCase(name.charAt(1)) && Character.isUpperCase(name.charAt(0))) {
            return name;
        }
        char chars[] = name.toCharArray();
        chars[0] = Character.toLowerCase(chars[0]);
        return new String(chars);
    }
}
